# coding:utf-8
# __user__ = hiicy redldw
# __time__ = 2019/9/5
# __file__ = visualization
# __desc__ =
import numpy as np
import keras.backend as K
from keras.preprocessing import image
import matplotlib.pyplot as plt
import keras.applications.resnet50
from keras.applications.vgg16 import preprocess_input, decode_predictions
image_path = r'E:\memory\Bigphoto\20181226165707122.jpeg'
img = image.load_img(image_path,target_size=(224,224))
img = image.img_to_array(img)
x = np.expand_dims(img, axis=0)  # 添加一个维度，将数组转化为（1， 224， 224， 3）的形状批量
x = preprocess_input(x)

from keras.models import load_model
model = keras.applications.vgg16.VGG16(weights='imagenet')
preds = model.predict(x)

# print('Predicted:', decode_predictions(preds, top=3)[0])
# print(model.output.shape)

###########   (类激活图) 动态可视化      #######
# torch版理解 https://www.kesci.com/home/project/5e903797e7ec38002d0161ad
# 应用Grad-CAM算法
# Grad-CAM通过对最后一层特征图进行加权求和得到heatmap，整个CAM系列的主要研究就在于这个加权求和中的权值从那里来。
# Grad-CAM是对特征图进行求梯度!，将每一张特征图上的梯度求平均得到权值（特征图的梯度是element-wise的）

african_elephant_output = model.output[:, 386]  # 预测向量中的非洲象元素

last_conv_layer = model.get_layer('block5_conv3')  # block5_conv3层的输出特征图，它是VGG16的最后一个卷积层
# african_elephant_output当损失看，后面当变量看，求变量的梯度
# REW:利用输出 求某一层的激活
grads = K.gradients(african_elephant_output, last_conv_layer.output)[0]  # 非洲象类别相对于block5_conv3输出特征图的梯度
# 平均通道
pooled_grads = K.mean(grads, axis=(0, 1, 2))  # 形状是（512， ）的向量，每个元素是特定特征图通道的梯度平均大小 FAQ:相当于重要性
# 这个函数允许我们获取刚刚定义量的值：对于给定样本图像，pooled_grads和block5_conv3层的输出特征图
iterate = K.function([model.input], [pooled_grads, last_conv_layer.output[0]])
# REW:卷积层响应值最高
pooled_grads_value, conv_layer_output_value = iterate([x])  # 给我们两个大象样本图像，这两个量都是Numpy数组

for i in range(512):
    conv_layer_output_value[:, :, i] *= pooled_grads_value[i]  # REW:将特征图数组的每个通道乘以这个通道对大象类别重要程度
heatmap = np.mean(conv_layer_output_value, axis=-1)  # 得到的特征图的逐通道的平均值即为类激活的热力图

heatmap = np.maximum(heatmap, 0)  # heatmap与0比较，取其大者

heatmap /= np.max(heatmap)

plt.matshow(heatmap)

plt.show()

import cv2

img = cv2.imread(image_path)  # 用cv2加载原始图像

heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0]))  # 将热力图的大小调整为与原始图像相同

heatmap = np.uint8(255 * heatmap)  # 将热力图转换为RGB格式

heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)  # 将热力图应用于原始图像

superimposed_img = heatmap * 0.4 + img  # 这里的0.4是热力图强度因子
cv2.imwrite(r'E:\memory\Bigphoto\elephant_cam.jpg', superimposed_img)  # 将图像保存到硬盘



# last_conv_layer = model.get_layer('block5_conv3')
# plot_fea_layer = K.function([model.layers[0].input],[model.layers[1].output,last_conv_layer.output[0]])
# lowf,upf = plot_fea_layer([x])
# print(lowf.shape,upf.shape)
# for _ in range(32):
#     lowf_img = upf[:, :, _]  # REW:这里是展示的所有通道特征，这里展示了16个通道
#     # up_img = upf[:,:,_]
#     plt.subplot(8,4, _ + 1)
#     plt.imshow(lowf_img, cmap='gray')
#     # plt.imshow(up_img, cmap='gray')
#     plt.axis('off')
# plt.show()

######## FAQ:滤波器权重可视化？
def generate_pattern(layer_name, filter_index, size=150):
    # 为滤波器的可视化定义损失张量
    layer_output = model.get_layer(layer_name).output
    loss = K.mean(layer_output[:, :, :, filter_index])
    # 获取损失相对于输入的梯度
    grads = K.gradients(loss, model.input)[0]
    grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)
    iterate = K.function([model.input], [loss, grads])

    # 通过梯度下降让损失最大化
    input_img_data = np.random.random((1, size, size, 3)) * 20 + 128
    step = 1
    for i in range(100):
        loss_value, grads_value = iterate([input_img_data])
        input_img_data += grads_value * step

    img = input_img_data[0]
    plt.imshow(img)
    plt.show()
    return

# generate_pattern("block5_conv3",10)